﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using TroopDeployment = System.Collections.Generic.IEnumerable<int>;

namespace WaterlooAnalysis
{
    public static class Extensions
    {
        public static bool Win(this List<int> firstCombination, List<int> secondCombination)
        {
            int count = firstCombination.Where((v, i) => v > secondCombination[i]).Count();
            return count > firstCombination.Count / 2;
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            //AnalyzeWaterlooMove(new[] {34, 34, 11, 11, 10});
            CalculateMostWinsSolution(total: 15, n: 5);
        }

        private static void AnalyzeWaterlooMove(IEnumerable<int> ints)
        {
            var combinations = Combinations(100, 5).ToList();

            Console.WriteLine("Waterloo total moves: {0}", combinations.Count);

            var combinationToTest = new List<int>(ints);
            int wins = combinations.Count(combinationToTest.Win);

            Console.WriteLine("Wins: {0}", wins);
            Console.WriteLine("Percentage of wins: {0}%", wins * 100 / combinations.Count);
            Console.ReadKey();
        }

        private static void CalculateMostWinsSolution(int total, int n)
        {
            int maxWins = 0;
            var betterCombination = new List<int>();

            var combinations = Combinations(total, n).ToList();

            foreach (var firstCombination in combinations)
            {
                int wins = combinations.Count(secondCombination => firstCombination.Win(secondCombination));

                //Print(firstCombination, wins);

                if (wins > maxWins)
                {
                    betterCombination = firstCombination.ToList();
                    maxWins = wins;
                }
            }

            Console.WriteLine("\nTotal Number of Combinations: {0}", combinations.Count());
            Console.WriteLine("\nBetter combination:");
            Print(betterCombination, maxWins);

            Console.ReadKey();
        }

        private static IEnumerable<List<int>> Combinations(int total, int n)
        {
            var v = Enumerable.Repeat(0, n).ToList();
            int sum = 0;

            while (true)
            {
                v[n - 1] = total - sum;
                yield return v.ToList();

                int i = n - 2;

                do
                {
                    v[i]++; sum++;

                    if (sum <= total) break;

                    sum -= v[i]; v[i] = 0; --i;
                }
                while (i >= 0);

                if (i < 0) break;
            }
        }

        private static void Print(IEnumerable<int> ints, int wins)
        {
            var sb = new StringBuilder();

            foreach (var i in ints)
            {
                sb.Append(i + " ");
            }

            sb.Append("  Wins: " + wins);

            Console.WriteLine(sb);
        }

        #region Ian Scruby

        private static IEnumerable<TroopDeployment> EnumerateDeployments(int availableTroops, int numberOfBattlefields)
        {
            if (numberOfBattlefields == 1)
            {
                yield return AsEnumerable(availableTroops);
            }
            else if (numberOfBattlefields > 1)
            {
                foreach (int troopsInFirstField in RangeFromToInclusive(0, availableTroops))
                {
                    foreach (TroopDeployment troopsInOtherFields in EnumerateDeployments(availableTroops - troopsInFirstField, numberOfBattlefields - 1))
                    {
                        yield return AsEnumerable(troopsInFirstField).Concat(troopsInOtherFields);
                    }
                }
            }
        }

        private static IEnumerable<T> AsEnumerable<T>(T obj)
        {
            yield return obj;
        }
        
        private static IEnumerable<int> RangeFromToInclusive(int start, int end)
        {
            return Enumerable.Range(start, 1 + end - start);
        }

        #endregion
    }
}
